#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <utility>
#include <algorithm>
#include <boost/format.hpp>
#include <boost/bind.hpp>
#include <json.hh>
#include "FlightChessBoardServer.h"
#include "model/FlightChessBoardErrDesc.h"

using namespace JSON;

namespace control_flight_chess_board
{
	FlightChessBoardServer::FlightChessBoardServer(EventLoop *loop,const InetAddress &listenAddr,int numThreads)
		: server_(loop,listenAddr,"flight chess board server"),numThreads_(numThreads),startTime_(Timestamp::now())
	{
		//设置建立连接后的处理回调（可以进行基本的判断设置）
		server_.setConnectionCallback(boost::bind(&FlightChessBoardServer::OnConnection,this,_1));
		//设置消息处理的回调（具体的业务处理）
		server_.setMessageCallback(boost::bind(&FlightChessBoardServer::OnMessage,this,_1,_2,_3));
		//写完成后的回调，如果不需要保持连接需要进行断开
		server_.setWriteCompleteCallback(boost::bind(&FlightChessBoardServer::OnWriteComplete,this,_1));
	}

	FlightChessBoardServer::~FlightChessBoardServer()
	{
		//至少做一些基本的回收工作
	}

	void
	FlightChessBoardServer::Start()
	{
		//这里就启动线程池
		LOG_INFO << "starting " << numThreads_ << " threads.";
		threadPool_.setThreadInitCallback(boost::bind(&ThreadInit));
		threadPool_.start(numThreads_);
		server_.start();//启动tcp基本服务器
	}

	void
	FlightChessBoardServer::OnConnection(const TcpConnectionPtr &conn)
	{
		LOG_INFO << conn->localAddress().toIpPort() << " -> "
			 << conn->peerAddress().toIpPort() << " is "
			 << (conn->connected() ? "UP" : "DOWN");
		//连接建立
		if(conn->connected()) {
			//设置该连接为非延迟
			conn->setTcpNoDelay(true);
			connections_.insert(make_pair(conn->peerAddress().toIpPort().c_str(),conn));
		} else {
			connections_.erase(conn->peerAddress().toIpPort().c_str());
		}
	}
	
	void
	FlightChessBoardServer::OnMessage(const TcpConnectionPtr &conn,Buffer *buf,Timestamp)
	{
		//解包送入到计算线程中
		static vector<uint8_t> v;//当做数组用
		//这里将接收到的数据复制到会话自己的缓冲中
		//这里主要有两个事务性的逻辑处理
		//要么是对服务器的请求型事务，要么是和其它
		//设备的通信型事务
		//下个版本客户端的管理需要移入到一个专用类来处理
		//同时在类中开启线程来进行发送，做到降低主event loop
		//的延迟
		while(buf->readableBytes() >= sizeof(BaseConnInfoHead)) {
			const void *data = buf->peek();
			const BaseConnInfoHead *prh = static_cast<const BaseConnInfoHead *>(data);
			uint16_t cmd = muduo::net::sockets::networkToHost16(prh->cmd);
			uint16_t len = muduo::net::sockets::networkToHost16(prh->len);
			if(len > max_buf_len || len < 0) {
				LOG_ERROR << "Invalid length " << len;
				conn->forceCloseWithDelay(1);
				break;
			} else if(cmd == MSG_TO_SERVER) {
				//服务请求处理
				//接收到数据后调用相关函数进行处理
				uint32_t procLen = sizeof(BaseConnInfoHead) + len;
				if(buf->readableBytes() < procLen) {
					LOG_ERROR << "Invalid package length " << procLen << " real len " << buf->readableBytes();
					conn->forceCloseWithDelay(1);
					break;
				}
				v.clear();
				v.resize(len);
				//目的是去掉请求头将数据送入线程池处理
				copy((const uint8_t *)data + sizeof(BaseConnInfoHead),(const uint8_t *)data + (procLen - 1),v.begin());
				threadPool_.run(boost::bind(&FcbSessionRecvCallback,conn,v));
				buf->retrieve(procLen);
			} else if(cmd == MSG_TO_OTHER_CLIENT) {
				//转发请求处理结构是|BaseConnInfoHead|SingleTransmitInfoHead|...|
				//获取该请求的数据所有长度
				uint32_t procLen = sizeof(BaseConnInfoHead) + len;
				if(buf->readableBytes() < procLen) {
					LOG_ERROR << "Invalid package length " << procLen << " real len " << buf->readableBytes();
					conn->forceCloseWithDelay(1);
					break;
				}
				//需要解包，然后进行转发的解码将消息进行转发
				const void *p = buf->peek() + sizeof(BaseConnInfoHead);
				const SingleTransmitInfoHead *pstrh = \
					static_cast<const SingleTransmitInfoHead *>(p);
				//通过ip和端口找到匹配的客户端转发出去
				char ip_str[32] = {0};socklen_t ip_str_size = sizeof(ip_str);
				::inet_ntop(AF_INET, &pstrh->d_ip, ip_str, ip_str_size);
				boost::format ip_port_fmt("%s:%d");
				ip_port_fmt % ip_str % sockets::networkToHost16(pstrh->d_port);
				LOG_INFO << "ip:port " <<ip_port_fmt.str();
				ConnectionMap::iterator it = connections_.find(ip_port_fmt.str());
				if(it != connections_.end()) {
					//进行转发，直接带上请求头
					it->second->send(data,procLen);
				} else {
					//没找到目标客户端向目的客户端返回错误的信息
					ErrResponseMsgMake(DST_CLIENT_UNKNOWN,
							DST_CLIENT_UNKNOWN_DESC,v);
					conn->send(&v[0],v.size());
				}
				//无论是否转发成功，都要偏移看是否还有其它请求
				buf->retrieve(procLen);
			}  else if(cmd == MSG_TO_OTHER_CLIENTS) {
				//这个需要首先客户端要有正确的登陆行为
				//然后是根据获取的用户列表来进行转发
				//把基本的頭去掉然後送入到解析函數中進一步解析
				//获取该请求的数据所有长度
				uint32_t procLen = sizeof(BaseConnInfoHead) + len;
				if(buf->readableBytes() < procLen) {
					LOG_ERROR << "Invalid package length " << procLen << " real len " << buf->readableBytes();
					conn->forceCloseWithDelay(1);
					break;
				}
				//獲取基礎信息
				//數據結構應該是{"transmit_msg":"move chess","role_to":"<停机坪|塔台>"}
				const void *p = buf->peek() + sizeof(BaseConnInfoHead);
				//找到ip:port對應的角色信息
				Value b_info_v = parse_string((char *)p);
				Value f_type = b_info_v["transmit_msg"];
				if((std::string)f_type == "move chess") {
					//獲取角色信息
					//注意這裏有個比較隱蔽的問題就是角色
					//信息如果是無限的情況，可能會導致內
					//存泄露的情況
					Value role_type = b_info_v["role_to"];
					ConnectionBaseInfoMap::iterator info_it = connectionsInfo_.find((std::string)role_type);
					if(info_it != connectionsInfo_ .end()) {
						//根據連接的基本信息找到所有需要轉發的客戶端
						std::list<std::string> &role_list = info_it->second;
						if(!role_list.empty()) {
							//根據role_list記錄的信息從connections_中找到對應的句柄轉發出去
							//這裏就直接先將ip:port的值取出來這個值按照json來解析
							std::list<std::string>::iterator transmit_it;
							bool is_trasmit_err = false;
							for(transmit_it = role_list.begin();transmit_it != role_list.end();) {
								Value conn_v = parse_string(transmit_it->c_str());
								Value ip_port_v = conn_v["ip:port"];
								ConnectionMap::iterator conn_it = connections_.find((std::string)ip_port_v);
								if(conn_it != connections_.end()) {
									if(conn_it->second->connected()) {
										//进行转发，直接带上请求头
										if(conn_it->second != conn) {
											//是所有除去自己的其它客戶端
											conn_it->second->send(data,procLen);	
										}
									} else {
										//進行連接句柄的刪除
										connections_.erase(conn_it);
									}
									//自增操作
									++transmit_it;
								} else {
									//没找到目标客户端向目的客户端返回错误的信息
									is_trasmit_err = true;
									//說明這條消息應該刪除，否則下次還會存在
									//而且會造成內存泄露
									role_list.erase(transmit_it++);
								}
							}
							if(is_trasmit_err) {
								LOG_INFO << "is_trasmit_err"; 
								ErrResponseMsgMake(DST_CLIENT_UNKNOWN,
										DST_CLIENT_UNKNOWN_DESC,v);
								conn->send(&v[0],v.size());
							}
						} else {
							LOG_INFO << "!role_list.empty()";
							//空的返回錯誤的消息
							ErrResponseMsgMake(DST_CLIENT_UNKNOWN,
									DST_CLIENT_UNKNOWN_DESC,v);
							conn->send(&v[0],v.size());
							//爲空的角色列表可以刪除，以免造成安全隱患
							connectionsInfo_.erase(info_it);
						}
					} else {
						//没有找到，说明没有对应的角色存在
						ErrResponseMsgMake(ROLE_NAME_UNKNOWN,
								ROLE_NAME_UNKNOWN_DESC,v);
						conn->send(&v[0],v.size());
					}
				} else {
					//这里说明转发消息没有满足要求
					ErrResponseMsgMake(TRANSMIT_MSG_TYPE_UNKNOWN,
							TRANSMIT_MSG_TYPE_UNKNOWN_DESC,v);
				}
				//進行數據偏移
				buf->retrieve(procLen);
			} else if(cmd == MSG_CLIENT_LOGIN) {
				//對數據進行進一步的解析，記錄一些基本的信息
				//接收到数据后调用相关函数进行处理
				uint32_t procLen = sizeof(BaseConnInfoHead) + len;
				if(buf->readableBytes() < procLen) {
					LOG_ERROR << "Invalid package length " << procLen << " real len " << buf->readableBytes();
					conn->forceCloseWithDelay(1);
					break;
				}
				const void *p = buf->peek() + sizeof(BaseConnInfoHead);
				//需要對一些基本信息進行記錄，主要是構造json來存儲
				Value l_info_v = parse_string((char *)p);
				//後面可能會需要進行一些登陸的驗證，如果錯誤需要返回錯誤的響應
				///////////////////////////////////////////////////////////////////////////////////
				//下面是進行基本信息添加的管理工作
				//{"request_type":"<login name>","name":"<登陆名称>","role":"<停机坪|塔台>"}
				//构造新的json数据用于存放角色->相关客户端的信息用于按照角色推送数据
				//{"login name":"<登陆名称>","ip:port":"<ip:port格式的字符串>"}
				boost::format conn_info_fmt("{\"login name\":\"%s\",\"ip:port\":\"%s\"}");
				conn_info_fmt % (std::string)l_info_v["name"] % conn->peerAddress().toIpPort().c_str();
				//先查找下对应角色是否已经有用户在线
				ConnectionBaseInfoMap::iterator info_it = connectionsInfo_.find((std::string)l_info_v["role"]);
				if(info_it != connectionsInfo_.end()) {
					//找到了列表
					std::list<std::string> &role_list = info_it->second;
					role_list.push_back(conn_info_fmt.str());
				} else {
					//新的角色
					//构建该新角色对应的客户端关系
					std::list<std::string> role_list;
					role_list.push_back(conn_info_fmt.str());
					connectionsInfo_.insert(make_pair((std::string)l_info_v["role"],role_list));
				}
				if(!allconnectionsInfo_.empty()) {
					//现在要构造响应包，返回一个已经登陆的客户列表，用于客户端展示
					//BaseConnInfoHead|{"response_type":"login","f_list_len":len(friends_list),"friends_list":[{"f_name":"名字","ip:port":"ip:端口","role":"<停机坪|塔台>"},{...},...]}
					boost::format login_res_info_fmt("{\"response_type\":\"login\",\"f_list_len\":%d,\"friends_list\":[%s]}");
					std::string friends_list;
					ConnectionUserInfoMap::iterator cur_usr_it;
					ConnectionMap::iterator conn_it;
					for(cur_usr_it = allconnectionsInfo_.begin();cur_usr_it != allconnectionsInfo_.end();) {
						//這裏還要判斷下對應的連接句柄是否可用
						conn_it = connections_.find(cur_usr_it->first);
						if(conn_it != connections_.end()) {
							if(conn_it->second->connected()) {
								//要處於連接狀態才行
								friends_list += cur_usr_it->second;
								if(++cur_usr_it != allconnectionsInfo_.end()) {
									friends_list += ",";
								}
							} else {
								//自增操作
								++cur_usr_it;
								//說明該連接已經無效，可以刪除了
								connections_.erase(conn_it);
							}
						}  else {
							//連接無效，相關信息可以刪除了
							//注意這裏必須要處理allconnectionsInfo_列表
							//否則會有內存泄露的情況
							allconnectionsInfo_.erase(cur_usr_it++);
						}
					}
					login_res_info_fmt % allconnectionsInfo_.size() % friends_list;
					std::string login_res_info_msg =  login_res_info_fmt.str();
					v.clear();
					v.resize(sizeof(BaseConnInfoHead) + login_res_info_msg.size() + 1);
					BaseConnInfoHead *h = (BaseConnInfoHead *)&v[0];
					h->cmd = muduo::net::sockets::hostToNetwork16(MSG_TO_CLIENT);
					h->len = muduo::net::sockets::hostToNetwork16(login_res_info_msg.size() + 1);
					copy(login_res_info_msg.begin(),login_res_info_msg.end(),h->data);
					conn->send(&v[0],v.size());
				} else {
					//向客戶端發送響應確認消息主要是第一個登陸用戶需要特殊處理
					std::string login_res_info_msg("{\"response_type\":\"login\",\"f_list_len\":0,\"friends_list\":[]}");
					v.clear();
					v.resize(sizeof(BaseConnInfoHead) + login_res_info_msg.size() + 1);
					BaseConnInfoHead *h = (BaseConnInfoHead *)&v[0];
					h->cmd = muduo::net::sockets::hostToNetwork16(MSG_TO_CLIENT);
					h->len = muduo::net::sockets::hostToNetwork16(login_res_info_msg.size() + 1);
					copy(login_res_info_msg.begin(),login_res_info_msg.end(),h->data);
					conn->send(&v[0],v.size());
				}
				//構造基本的客戶信息結構，用於後面的推送
				boost::format cur_conn_info_fmt("{\"f_name\":\"%s\",\"ip:port\":\"%s\",\"role\":\"%s\"}");
				cur_conn_info_fmt % (std::string)l_info_v["name"] % conn->peerAddress().toIpPort().c_str() % (std::string)l_info_v["role"];
				if(!connections_.empty()) {
					//同时还要向所有已登陆的客户端推送新客户登陆消息
					//BaseConnInfoHead|{"response_type":"new client online","base_info":{"f_name":"名字","ip:port":"ip:端口","role":"<停机坪|塔台>"}}
					boost::format new_clinet_conn_info_fmt("{\"response_type\":\"new client online\",\"base_info\":%s}");
					new_clinet_conn_info_fmt % cur_conn_info_fmt.str();
					std::string new_clinet_conn_info_msg = new_clinet_conn_info_fmt.str();
					size_t v_len = sizeof(BaseConnInfoHead) + new_clinet_conn_info_msg.size() + 1;
					v.clear();
					v.resize(v_len);
					BaseConnInfoHead *h = (BaseConnInfoHead *)&v[0];
					h->cmd = muduo::net::sockets::hostToNetwork16(MSG_TO_CLIENT);
					h->len = muduo::net::sockets::hostToNetwork16(new_clinet_conn_info_msg.size() + 1);
					copy(new_clinet_conn_info_msg.begin(),new_clinet_conn_info_msg.end(),h->data);
					ConnectionMap::iterator conn_it;
					for(conn_it = connections_.begin();conn_it != connections_.end();) {
						//向連接上的，且不是本機的其它客戶端轉發消息
						if(conn_it->second->connected()) {
							if(conn_it->second != conn) {
								conn_it->second->send(&v[0],v.size());
							}
							++conn_it;
						} else {
							connections_.erase(conn_it++);
						}
					}
				}
				//向当前用户列表map注册相关消息，用于后面的一些转发
				allconnectionsInfo_.insert(make_pair(conn->peerAddress().toIpPort().c_str(),cur_conn_info_fmt.str()));
				//偏移該包的長度
				buf->retrieve(procLen);
			} else {
				LOG_ERROR << "Invalid cmd " << cmd;
				//未知的命令，返回错误信息
				ErrResponseMsgMake(CMD_TYPE_UNKNOWN,
						CMD_TYPE_UNKNOWN_DESC,v);
				conn->send(&v[0],v.size());
				conn->forceCloseWithDelay(1);
				break;
			}
		}
	}

	void
	FlightChessBoardServer::OnWriteComplete(const TcpConnectionPtr &conn)
	{
		//写结束，可以在这里主动断开连接
		return;
	}

	void
	FlightChessBoardServer::ThreadInit()
	{
		//运行在线程中
		assert(FcbSession::pointer() == NULL);
		FcbSession::instance();
		assert(FcbSession::pointer() != NULL);
	}

	void
	FlightChessBoardServer::ErrResponseMsgMake(uint32_t err_no,const char *err_msg_cstr,vector<uint8_t> &dst_v)
	{
		//进行包的组装构建到dst_v中
		std::string err_msg(err_msg_cstr);
		uint32_t eml = err_msg.size() + 1;
		dst_v.clear();
		dst_v.resize(sizeof(BaseConnInfoHead) + sizeof(ErrInfoHead) + eml);
		ErrInfoHead *err_info = (ErrInfoHead *)(&dst_v[sizeof(BaseConnInfoHead)]);
		BaseConnInfoHead *req_info = (BaseConnInfoHead *)(&dst_v[0]);
		err_info->err_no = muduo::net::sockets::hostToNetwork32(err_no);
		err_info->err_msg_len = muduo::net::sockets::hostToNetwork32(eml);
		copy(err_msg.begin(),err_msg.end(),err_info->msg);
		req_info->cmd = muduo::net::sockets::hostToNetwork16(MSG_ERR);
		uint32_t len = sizeof(ErrInfoHead) + eml;
		req_info->len = muduo::net::sockets::hostToNetwork16(len);
	}

	void
	FlightChessBoardServer::FcbSessionOutputDataCallback(const TcpConnectionPtr &conn,const char *buf,size_t len)
	{
		//需要进行包的构建
		static vector<uint8_t> s_buf;
		s_buf.clear();
		s_buf.resize(sizeof(BaseConnInfoHead) + len);
		BaseConnInfoHead *h_info = (BaseConnInfoHead *)&s_buf[0];
		h_info->cmd = muduo::net::sockets::hostToNetwork16(MSG_TO_CLIENT);
		h_info->len = muduo::net::sockets::hostToNetwork16(len);
		copy(buf,buf + (len - 1),h_info->data);
		//运行在线程中
		conn->send(&s_buf[0],s_buf.size());
	}

	void
	FlightChessBoardServer::FcbSessionInputDataCallback(const vector<uint8_t> &buf)
	{
		//运行在线程中
		FcbSession::instance().Init((const char *)&buf[0],buf.size());
	}

	void
	FlightChessBoardServer::FcbSessionOutputErrMsgCallback(const TcpConnectionPtr &conn,uint32_t err_no,const char *err_msg_str)
	{
		vector<uint8_t> err_msg_buf;
		//构建错误的消息包并发送
		ErrResponseMsgMake(err_no,err_msg_str,err_msg_buf);
		conn->send(&err_msg_buf[0],err_msg_buf.size());
	}

	void
	FlightChessBoardServer::FcbSessionRecvCallback(const TcpConnectionPtr &conn,const vector<uint8_t> &buf)
	{
		//运行在线程中
		//设置本次会话处理输出回调用于输出结果
		FcbSession::instance().SetFcbSessionOutputDataCallback(boost::bind(&FcbSessionOutputDataCallback,conn,_1,_2));
		//设置本次会话处理输入回调用于接收数据
		FcbSession::instance().SetFcbSessionInputDataCallback(boost::bind(&FcbSessionInputDataCallback,buf));
		//错误消息发送回调
		FcbSession::instance().SetFcbSessionOutputErrMsgCallback(boost::bind(&FcbSessionOutputErrMsgCallback,conn,_1,_2));
		//进行本次会话的处理
		FcbSession::instance().Process();
	}
	//对于登陆需要进行特殊的处理
};
